{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# \\__init_subclass__()\n",
    "\n",
    "重写\\__init_subclass__方法， 先看下效果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "__init_subclass__ <class '__main__.A'> {'name': 'satori', 'age': 16}\n"
     ]
    }
   ],
   "source": [
    "class Hook:\n",
    "\n",
    "    def __init_subclass__(cls, **kwargs):\n",
    "        print(\"__init_subclass__\", cls, kwargs)\n",
    "\n",
    "\n",
    "class A(Hook, name=\"satori\", age=16):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "首先我们定义了一个Hook类，然后让A这个类继承它。**发现我们还没有没有实例化，而是在创建类的时候就有输出结果了。**\n",
    "\n",
    "对于一个类，如果这个类被作为父类继承，那么会触发其内部的\\__init_subclass__方法，这里的Hook被A继承，那么Hook中的\\__init_subclass__就会被触发。而且看到，里面的cls，就是我们的A，也就是继承它的类，**kwargs，就是我们额外传递的参数。\n",
    "\n",
    "但是我们发现，第一个参数不是self，而是cls，而且这个cls还不是我们的Hook，而是继承它的类。其实这个方法是隐式的被classmethod装饰了\n",
    "\n",
    "\n",
    "有时候我们想控制类的生成过程，怎么办呢？显然可以通过元类的的方式，但是如果场景比较简单，也没必要使用元类。直接使用\\__init_subclass__即可"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('satori', 16)"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class Hook:\n",
    "\n",
    "    def __init_subclass__(cls, **kwargs):\n",
    "        for k, v in kwargs.items():\n",
    "            type.__setattr__(cls, k, v)\n",
    "\n",
    "\n",
    "class A(Hook, name=\"satori\", age=16):\n",
    "    pass\n",
    "\n",
    "\n",
    "a = A()\n",
    "\n",
    "a.name, a.age"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到，我们在不使用元类的情况下，通过__init_subclass__实现了类的自定义过程。当然这比较简单，也可以实现更复杂的逻辑，在某些场景下，可以替代元类。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "# 获取所有子类 \n",
    "\n",
    "新型类（即objectPython中默认的from的子类）具有一种\\__subclasses__返回子类的方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Foo(object):\n",
    "    pass\n",
    "\n",
    "\n",
    "class Bar(Foo):\n",
    "    pass\n",
    "\n",
    "\n",
    "class Baz(Foo):\n",
    "    pass\n",
    "\n",
    "\n",
    "class Bing(Bar):\n",
    "    pass\n",
    "\n",
    "\n",
    "class Cing(Bing):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['Bar', 'Baz']\n"
     ]
    }
   ],
   "source": [
    "# 这是子类的名称\n",
    "print([cls.__name__ for cls in Foo.__subclasses__()])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[<class '__main__.Bar'>, <class '__main__.Baz'>]\n"
     ]
    }
   ],
   "source": [
    "# 这是子类本身\n",
    "print(Foo.__subclasses__())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class '__main__.Foo'>\n",
      "<class '__main__.Foo'>\n"
     ]
    }
   ],
   "source": [
    "# 确认确实将子类Foo列为其基础\n",
    "for cls in Foo.__subclasses__():\n",
    "    print(cls.__base__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{<class '__main__.Cing'>, <class '__main__.Bing'>, <class '__main__.Bar'>, <class '__main__.Baz'>}\n"
     ]
    }
   ],
   "source": [
    "# 请注意，如果需要子类，则必须递归\n",
    "def all_subclasses(cls):\n",
    "    return set(cls.__subclasses__()).union(\n",
    "        [s for c in cls.__subclasses__() for s in all_subclasses(c)])\n",
    "\n",
    "print(all_subclasses(Foo))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "请注意，如果尚未执行子类的类定义（例如，如果尚未导入子类的模块），则该子类尚不存在，\\__subclasses__也找不到它。\n",
    "\n",
    "---\n",
    "\n",
    "# 通过字符串查找类对象\n",
    "\n",
    "如果确实有一个表示类名称的字符串，并且想要查找该类的子类，有两步：找到给定名称的类，然后使用\\__subclasses__上述方法查找子类。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.Foo"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "globals()['Foo']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "__main__.Foo"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "locals()['Foo']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果该类可以位于任何模块中，则你的名称字符串应包含完全限定的名称- ‘pkg.module.Foo’而不是just ‘Foo’。使用importlib加载类的模块，然后获取相应的属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('lxml.etree', 'HTML')"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import importlib\n",
    "\n",
    "name = 'lxml.etree.HTML'\n",
    "modname, _, clsname = name.rpartition('.')\n",
    "modname, clsname"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<cyfunction HTML at 0x00000242F2E58778>"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mod = importlib.import_module(modname)\n",
    "cls = getattr(mod, clsname)\n",
    "cls"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "position": {
    "height": "237.4px",
    "left": "1195.6px",
    "right": "20px",
    "top": "120px",
    "width": "320.4px"
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
